﻿// Copyright (c) egmkang wang. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace XServer.SDK.Net
{
    using System;
    using System.Reflection;
    using System.Collections.Generic;
    using DotNetty.Buffers;
    using DotNetty.Codecs;
    using XServer.Message;


    public sealed class Codec
    {
        static Dictionary<ushort, IMessage> parsers = new Dictionary<ushort, IMessage>();
        static Dictionary<string, ushort> msg2id = new Dictionary<string, ushort>();
        static OnceFlag load = new OnceFlag();

        public static ushort GetMessageID(string name)
        {
            if (parsers.Count == 0)
            {
                load.CallOnce(Load);
            }
            if (msg2id.ContainsKey(name))
            {
                return msg2id[name];
            }
            return 0;
        }
        public static IMessage NewMessage(ushort msgid, byte[] array)
        {
            if (parsers.Count == 0)
            {
                load.CallOnce(Load);
            }

            if (parsers.ContainsKey(msgid))
            {
                return parsers[msgid].Deserialize(array) as IMessage;
            }
            return null;
        }

        static void Load()
        {
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (var assembly in assemblies)
            {
                try
                {
                    if (assembly.GlobalAssemblyCache || assembly.IsDynamic)
                    {
                        continue;
                    }
                    LoadParserFromAssembly(assembly);
                }
                catch { }
            }
        }

        static void LoadParserFromAssembly(Assembly asm)
        {
            var types = asm.ExportedTypes;
            foreach (var typeInfo in types)
            {
                if (typeInfo.ToString().IndexOf(".Msg") >= 0)
                {
                    try
                    {
                        var instance = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeInfo) as IMessage;
                        if (instance == null) continue;

                        ushort msgid = 0;
                        foreach (var attr in instance.GetType().CustomAttributes)
                        {
                            if (attr.AttributeType.FullName == typeof(MsgIDAttribute).FullName)
                            {
                                foreach (var field_name in attr.ConstructorArguments)
                                {
                                    ushort.TryParse(field_name.Value.ToString(), out msgid);
                                    break;
                                }
                                break;
                            }
                        }
                        if (!parsers.ContainsKey(msgid))
                        {
                            parsers.Add(msgid, instance);
                        }
                        if (msgid != 0 && !msg2id.ContainsKey(instance.GetType().ToString()))
                        {
                            msg2id.Add(instance.GetType().ToString(), msgid);
                        }
                    }
                    catch (System.Exception ex)
                    {
                    }

                }
            }
        }
    }

    public interface BytesToMessage<Message>
    {
        void Decode(IByteBuffer input, List<Message> output);
    }

    public sealed class CSMsgDecoder : BytesToMessage<CSMessage>
    {
        public void Decode(IByteBuffer input, List<CSMessage> output)
        {
            input = input.WithOrder(ByteOrder.LittleEndian);
            output.Clear();
            CSHead head = new CSHead();

            while (input.ReadableBytes >= Constant.MIN_CS_HEAD_LEN)
            {
                input.MarkReaderIndex();
                head.length = input.ReadInt();
                head.msgid = (ushort)input.ReadShort();

                if (head.RealLength() > input.ReadableBytes)
                {
                    input.ResetReaderIndex();
                    return;
                }
                if (head.RealLength() > Constant.MAX_MSG_LEN)
                {
                    throw new TooLongFrameException("");
                }

                IByteBuffer buffer = input.SliceAndRetain(head.RealLength());
                var bytes = buffer.ReadSlice(buffer.ReadableBytes).ToArray();
                output.Add(new CSMessage(head.msgid, bytes, Platform.GetMilliSeconds()));
                buffer.Release();
            }
        }
    }
}
